/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package image

import (
	"bytes"
	"errors"
	"gitee.com/tugoer/orivil/mux"
	"github.com/nfnt/resize"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"time"
	"gitee.com/tugoer/orivil/param"
	"os"
	"io/ioutil"
	"strconv"
	"strings"
)

const (
	ImageDir = "images"
)

var staticServer = http.FileServer(http.Dir("."))

// 获得缩略图文件名, 不管什么图片类型, 统统返回 jpg 格式文件名
// getImageJpegThumb("/images/2008/08/01/foobar.png", 600, 700) => "/images/2008/08/01/foobar-600-700.jpg"
func getImageJpegThumb(file string, width, height, quality int) string {
	fname := filepath.Base(file)
	ext := filepath.Ext(fname)
	return file[:len(file)-len(fname)] + fname[:len(fname) - len(ext)] + "-" +
		strconv.Itoa(width) + "-" + strconv.Itoa(height) + "-" + strconv.Itoa(quality) + ".jpg"
}

var Actions = []*mux.Action{
	// 图片静态服务
	{
		Method: "GET", Route: "images/", Name: "Serve Image File", Params: mux.Parameters{{In:mux.Query, Schema:&serveImage{}}},
		Handler: func(ctx *mux.Context) {
			p := &serveImage{}
			ctx.WarnParse(p)
			if p.W != 0 || p.H != 0 {
				if p.Q == 0 {
					p.Q = 100
				}
				file := strings.TrimPrefix(ctx.URL.Path, "/")
				thumbFile := getImageJpegThumb(file, p.W, p.H, p.Q)
				_, err := os.Stat(thumbFile)
				// 缩略图不存在则生成新的缩略图
				if os.IsNotExist(err) {
					data, err := ioutil.ReadFile(file)
					if err != nil {
						ctx.ErrWarning(err)
					} else {
						buf := bytes.NewBuffer(data)
						img, _, err := image.Decode(buf)
						if err != nil {
							ctx.ErrWarning(err)
						} else {
							img = resize.Resize(uint(p.W), uint(p.H), img, resize.Lanczos3)
							buf = bytes.NewBuffer(nil)
							err = jpeg.Encode(buf, img, &jpeg.Options{Quality: p.Q})
							if err != nil {
								ctx.ErrWarning(err)
							} else {
								err = ioutil.WriteFile(thumbFile, buf.Bytes(), 0777)
								if err != nil {
									ctx.ErrWarning(err)
								}
							}
						}
					}
				}
				http.ServeFile(ctx.Response, ctx.Request, thumbFile)
			} else {
				staticServer.ServeHTTP(ctx.Response, ctx.Request)
			}
		},
		ApiDoc: &mux.ApiDoc{
			Tags:    []string{Images},
			Summary: "图片静态服务",
			Description: "可通过此接口生成缩略图, 点击查看[images](/images)",
		},
	},
	{
		Name: "查询图片信息", Method: "GET", Route: "image-info",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &getImageInfo{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &getImageInfo{}
			ctx.WarnParse(p)
			img := GetImage(p.ID)
			ctx.SendJson(map[string]*Image{"image": img})
		},
		ApiDoc: &mux.ApiDoc{
			Tags:    []string{Images},
			Summary: "获得图片信息",
			Responses: mux.Responses{
				"200": {
					Description: "success",
					Example: map[string]Image{"image": {}},
				},
			},
		},
	},
	// 查询时间段内图片总数
	{
		Method: "GET", Route: "total-images", Name: "Query Image",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &countImage{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &countImage{}
			ctx.WarnParse(p)
			var start, end *time.Time
			if p.StartTime != "" {
				s, err := time.Parse("2006-01-02", p.StartTime)
				if err != nil {
					ctx.ErrWarning(err)
				}
				start = &s
			}
			if p.EndTime != "" {
				e, err := time.Parse("2006-01-02", p.EndTime)
				if err != nil {
					ctx.ErrWarning(err)
				}
				end = &e
			}
			total := CountImage(start, end, p.Label)
			ctx.SendJson(map[string]int{"total": total})
		},
		ApiDoc: &mux.ApiDoc{
			Tags:    []string{Images},
			Summary: "查询图片数量",
			Responses: mux.Responses{
				"200": {
					Description: "success",
					Example: map[string]int{"total": 30},
				},
			},
		},
	},

	// 按时间查询图片
	{
		Method: "GET", Route: "search-images", Name: "Query Image",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &queryImage{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &queryImage{}
			ctx.WarnParse(p)
			var start, end *time.Time
			if p.StartTime != "" {
				s, err := time.Parse("2006-01-02", p.StartTime)
				if err != nil {
					ctx.ErrWarning(err)
				}
				start = &s
			}
			if p.EndTime != "" {
				e, err := time.Parse("2006-01-02", p.EndTime)
				if err != nil {
					ctx.ErrWarning(err)
				}
				end = &e
			}
			imgs := QueryImage(start, end, p.Label, p.Desc, p.Limit, p.Offset)
			ctx.SendJson(imgs)
		},
		ApiDoc: &mux.ApiDoc{
			Tags:    []string{Images},
			Summary: "查询图片",
			Responses: mux.Responses{
				"200": {
					Description: "success",
					Example: []Image{{}},
				},
			},
		},
	},
	{
		Method: "POST", Route: "images", Name: "Create Image",
		Params: mux.Parameters{
			{
				In:        mux.Form,
				Schema:    &uploadImage{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &uploadImage{}
			var images []*Image
			p.Images = func(field string, header *multipart.FileHeader) (err error) {
				ext := filepath.Ext(header.Filename)
				if ext == "" {
					return errors.New("file extension is nil")
				}
				src, e := header.Open()
				if e != nil {
					return e
				}
				defer src.Close()
				buf := bytes.NewBuffer(nil)
				if p.Width != 0 || p.Height != 0 {
					img, t, e := image.Decode(src)
					if e != nil {
						return e
					}
					// 缩放
					img = resize.Resize(uint(p.Width), uint(p.Height), img, resize.Lanczos3)
					switch t {
					case "png":
						err = png.Encode(buf, img)
					default:
						err = jpeg.Encode(buf, img, &jpeg.Options{
							Quality: p.Quality,
						})
					}
					if err != nil {
						return err
					}
				} else {
					_, err = io.Copy(buf, src)
					if err != nil {
						return err
					}
				}

				f, e := NewImageFile("", ext)
				if e != nil {
					return e
				}
				defer f.Close()
				_, err = io.Copy(f, buf)
				if err != nil {
					return err
				}
				images = append(images, &Image{Url: filepath.ToSlash(f.Name())})
				return nil
			}
			ctx.WarnParse(p)

			for _, img := range images {
				img.Label = p.Label
				err := img.Create()
				if err != nil {
					ctx.ErrWarning(err)
				}
			}

			ctx.SendJson(map[string]interface{}{"images": images})
		},
		ApiDoc: &mux.ApiDoc{
			Tags:    []string{Images},
			Summary: "上传图片",
			Responses: mux.Responses{
				"200": {
					Description: "success",
					Example: map[string]interface{}{"images": []Image{{}}},
				},
			},
		},
	},
	{
		Method: "DELETE", Route: "image", Name: "Delete Image",
		Params: mux.Parameters{
			{
				In:     mux.Query,
				Schema: &delImage{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &delImage{}
			ctx.WarnParse(p)
			i := &Image{ID: p.ID}
			err := i.Delete()
			if err != nil {
				ctx.ErrWarning(err)
			}
			ctx.MsgSuccess("已删除", "")
		},
	},
}

type countImage struct {
	StartTime string `desc:"起始时间, 紧支持 '2006-01-02' 格式"`
	EndTime   string `desc:"结束时间, 紧支持 '2006-01-02' 格式"`
	Label string `desc:"图片标签"`
}

type queryImage struct {
	StartTime string `desc:"起始时间, 紧支持 '2006-01-02' 格式"`
	EndTime   string `desc:"结束时间, 紧支持 '2006-01-02' 格式"`
	Label string `desc:"图片标签"`
	Desc      bool   `desc:"是否降序排列"`
	Limit     int    `num:"1<=x<=30" num-msg:"1-30之间" required:"Limit 不能为空"`
	Offset    int    `required:"Offset 不能为空"`
}

type uploadImage struct {
	Width   int               `desc:"图片固定宽度"`
	Height  int               `desc:"图片固定高度"`
	Quality int               `desc:"图片质量(1-100), 默认50, 仅jpg格式图片有效"`
	Label string `desc:"图片标签"`
	Images  param.FileHandler `required:"未上传任何图片" mime:"image" mime-msg:"图片格式不正确" desc:"图片文件字段"`
}

type getImageInfo delImage

type delImage struct {
	ID int `required:"ID 不能为空"`
}

type serveImage struct {
	W int `param:"w" desc:"缩略图宽度"`
	H int `param:"h" desc:"缩略图高度"`
	Q int `param:"q" desc:"缩略图质量 1-100, 如果不传该参数, 则默认100"`
}
